﻿using H.Nets;
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace H.Net.Nets
{
    /// <summary>
    /// 表示一个TCP服务端
    /// </summary>
    public class HTcpServer : ITcpServer
    {
        /// <summary>
        /// 表示一个TCP被请求客户端
        /// </summary>
        public class Client : AbstractTcpClient
        {
            /// <summary>
            /// 客户端是否可用
            /// </summary>
            public override bool IsValid => Connected;

            /// <summary>
            /// 当前连接的客户端的标识符,可以被客户端更新
            /// </summary>
            public override string Identifier
            {
                get => _Identifier ?? _Socket.RemoteEndPoint.ToString();
                set
                {
                    if (Identifier != value)
                    {
                        Send(CreateTopic(), HFrameType.IDENTIFY, value);
                        _Identifier = value;
                        NotifyClientRename(Identifier, value);
                    }
                }
            }

            /// <summary>
            /// 当前可用的消息ID
            /// </summary>
            private int _CurTopicID = -1;

            /// <summary>
            /// 初始化一个服务器客户端
            /// </summary>
            /// <param name="tcpClient"></param>
            internal Client(Socket socket) : base(socket)
            {
                _FrameStream = new HFrameStream(_Socket);
                NotifyChangeStatus(true);
                StartRead(CancellationToken.None);
            }

            /// <summary>
            /// 建立连接,并开始从客户端读取消息
            /// </summary>
            public override void Connect()
            {
                throw new NotSupportedException("被请求客户端不可以调用此函数");
            }

            /// <summary>
            /// 新建一个主题(被请求客户端的主题ID是递减的)
            /// </summary>
            /// <returns></returns>
            public override int CreateTopic()
            {
                return _CurTopicID = _CurTopicID % int.MinValue - 1;
            }
        }

        #region 服务器事件
        /// <summary>
        /// 当有新的客户端连接上服务器发射此事件
        /// </summary>
        public event NewConnectionHandler NewConnection;

        /// <summary>
        /// 当有客户端的标识符发生改变,发射此事件
        /// </summary>
        public event ClientRenameHandler ClientRename;
        #endregion


        #region 服务器内部变量
        /// <summary>
        /// 连接的标识符和连接的映射
        /// </summary>
        private readonly ConcurrentDictionary<string, Client> _ClientMapping = new ConcurrentDictionary<string, Client>();

        /// <summary>
        /// 内部管理的套接字实例
        /// </summary>
        private readonly Socket _Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        /// <summary>
        /// 当前监听客户端连接的线程
        /// </summary>
        private Thread _Thread;

        #endregion

        /// <summary>
        /// 获取一个客户端的连接,以便主动向客户端发送消息
        /// </summary>
        /// <param name="identifier"></param>
        /// <returns></returns>
        public ITcpClient GetClient(string identifier)
        {
            if (_ClientMapping.TryGetValue(identifier, out Client client))
            {
                return client;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 初始化一个服务器
        /// </summary>
        /// <param name="port"></param>
        public HTcpServer(int port)
        {
            _Socket.Bind(new IPEndPoint(IPAddress.Any, port));
        }

        /// <summary>
        /// 开始监听端口
        /// </summary>
        public void Start()
        {
            if (_Thread != null)
            {
                return;
            }

            _Thread = new Thread(() =>
            {
                _Socket.Listen(1000);
                try
                {
                    while (true)
                    {
                        Socket socket = _Socket.Accept();
                        Client client = new Client(socket);
                        client.Rename += Client_OnClientRename;
                        _ClientMapping.TryAdd(client.Identifier, client);
                        NewConnection?.BeginInvoke(client, iResult =>
                        {
                            NewConnection.EndInvoke(iResult);
                        }, null);
                    }
                }
                catch (Exception err)
                {
                    _Thread = null;
                    Console.WriteLine("服务器监听线程发生异常" + err);
                }
            })
            { IsBackground = true };


            _Thread.SetApartmentState(ApartmentState.STA);
            _Thread.Start();
        }

        /// <summary>
        /// 处理改名事件
        /// </summary>
        /// <param name="oldName"></param>
        /// <param name="newName"></param>
        private void Client_OnClientRename(string oldName, string newName)
        {
            if (_ClientMapping.TryGetValue(oldName, out Client client))
            {
                _ClientMapping.TryAdd(newName, client);
                _ClientMapping.TryRemove(newName, out client);
                ClientRename?.Invoke(oldName, newName);
            }
        }

        /// <summary>
        /// 停止建立新的客户端连接
        /// </summary>
        public void Stop()
        {
            //FIXME:此处的处理可能不合适
            _Socket.Close();
        }

        /// <summary>
        /// 使当前线程开始等待服务端监听线程结束后返回
        /// </summary>
        public void LoopWait()
        {
            _Thread?.Join();
        }


        private bool IsDisposed = false;

        public void Dispose()
        {
            Dispose(true);
        }

        private void Dispose(bool disposing)
        {
            if (disposing)
            {

            }
            if (!IsDisposed)
            {
                _Socket.Close();
            }

            IsDisposed = true;
        }

        ~HTcpServer()
        {
            Dispose(false);
        }
    }
}
